SPB Git
1.3 KB · 17 lines typescript
1import fs from "node:fs";2import { handler, HttpError, uuid } from "@/lib/api";3import { db, schema } from "@/db";4import { and, eq } from "drizzle-orm";5import { absPath } from "@/lib/files/storage";6const INLINE = new Set(["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml", "application/pdf", "text/plain", "video/mp4", "audio/mpeg"]);7/** Authenticated file delivery (never served from a public dir). */8export const GET = handler(async ({ ws, params }) => {9  const id = uuid.parse(params.id);10  const [a] = await db.select().from(schema.attachments).where(and(eq(schema.attachments.id, id), eq(schema.attachments.workspaceId, ws.id)));11  if (!a) throw new HttpError(404, "Not found");12  const p = absPath(a.storagePath); if (!fs.existsSync(p)) throw new HttpError(410, "File missing on disk");13  const stream = fs.createReadStream(p);14  const mime = a.mime === "image/svg+xml" ? "image/svg+xml" : a.mime;15  return new Response(stream as any, { headers: { "content-type": mime, "content-length": String(a.size), "content-disposition": `${INLINE.has(a.mime) ? "inline" : "attachment"}; filename*=UTF-8''${encodeURIComponent(a.filename)}`, "cache-control": "private, max-age=31536000, immutable", "x-content-type-options": "nosniff", ...(a.mime === "image/svg+xml" ? { "content-security-policy": "script-src 'none'" } : {}) } });16});17
Files · main
Menu

Theme